package com.surge;

import java.util.Date;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.MetadataSources;
import org.hibernate.boot.registry.StandardServiceRegistry;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class SurgeTest {
	
	private SessionFactory sessionFactory;
	// 生产环境不允许 把session与transaction 设置成成员变量，因为涉及到并发问题
	private Session session;
	private Transaction transaction;
	
	// junit 方法init 与 destory。 这样只需要test中写主要的业务测试逻辑
	@Before
	public void init(){
		//1、 版本5.x	
		final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
				.configure() // configures settings from hibernate.cfg.xml
				.build();
		try {
			sessionFactory = new MetadataSources( registry ).buildMetadata().buildSessionFactory();
		}
		catch (Exception e) {
			StandardServiceRegistryBuilder.destroy( registry );
		}
		// 2. 创建一个 Session 对象
		session = sessionFactory.openSession();

		// 3. 开启事务
		transaction = session.beginTransaction();
	}
	@After
	public void destory(){
		// 5. 提交事务
		transaction.commit();

		// 6. 关闭 Session
		session.close();

		// 7. 关闭 SessionFactory 对象
		sessionFactory.close();
	}
	
	/*
	 * ============================    测试逻辑代码    ============================
	 */
	
	/**
	 * SESSION 一级缓存
	 */
	@Test
	public void testSession() {
		// 4. 执行操作
		News news = (News)session.get(News.class, 1);
		System.out.println(news);
		
		News news2 = (News)session.get(News.class, 1);
		System.out.println(news2);
		
		/*
		 * 以上操作只会执行一条sql，第二次查询使用的第一次查询的缓存。
		 */
	}
	
	/**
	 * SESSION 一级缓存。flush(),与数据库保持一致。不需要我们操作。
	 */
	/**
	 * flush: 使数据表中的记录和 Session 缓存中的对象的状态保持一致. 为了保持一致, 则可能会发送对应的 SQL 语句.
	 * 1. 在 Transaction 的 commit() 方法中: 先调用 session 的 flush 方法, 再提交事务
	 * 2. flush() 方法会可能会发送 SQL 语句, 但不会提交事务. 
	 * 3. 注意: 在未提交事务或显式的调用 session.flush() 方法之前, 也有可能会进行 flush() 操作.
	 * 1). 执行 HQL 或 QBC 查询, 会先进行 flush() 操作, 以得到数据表的最新的记录
	 * 2). 若记录的 ID 是由底层数据库使用自增的方式生成的, 则在调用 save() 方法时, 就会立即发送 INSERT 语句. 
	 * 因为 save 方法后, 必须保证对象的 ID 是存在的!（理解：save后执行修改，那么id如果不存在就无法执行？）
	 */
	@Test
	public void testFlush() {
		// 4. 执行操作
		News news = (News)session.get(News.class, 1);
		
		news.setAuthor("老李");
		
		// 以上操作在一个事务中，在源码中可以返现，事务提交 里面执行了fulsh()
		
//		session.flush(); // 不需要执行
		
		/*
		 * 会查询，在更新表中的记录。两条sql。因为flush(),与数据库保持一致
		 */
	}
	@Test
	public void testFlush2() {
		// 4. 执行操作
		News news = (News)session.get(News.class, 1);
		
		news.setAuthor("老李3");
		
		// QBC 查询
		@SuppressWarnings("deprecation")
		News news2 = (News) session.createCriteria(News.class).uniqueResult();
		System.out.println(news2);
		
		/*
		 * 在事务提交前，new2查询获得的数据是 老李3 ，但是数据库还没有更新。
		 */
	}
	@Test
	public void testFlush3(){
		News news = new News("Java", "SUN", new Date());
		session.save(news);
		/*
		 * 在事务提交前，执行insert操作
		 */
	}
	
	/**
	 * refresh(): 会强制发送 SELECT 语句, 以使 Session 缓存中对象的状态和数据表中对应的记录保持一致!
	 */
	@Test
	public void testRefresh(){
		News news = (News) session.get(News.class, 1);
		System.out.println(news);
		
		session.refresh(news); 
		System.out.println(news); 
		/*
		 * 执行两次select语句
		 */
	}
	
	/**
	 * clear(): 清理缓存
	 */
	@Test
	public void testClear(){
		News news1 = (News) session.get(News.class, 1);
		System.out.println(news1);
		
		session.clear();
		
		News news2 = (News) session.get(News.class, 1);
		System.out.println(news2); 
		/*
		 * 执行两次select语句
		 */
	}
	

}
